package com.hexiang.service.impl;

import com.hexiang.service.ElasticSearchService;
import com.hexiang.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 *
 */
@Component
@Slf4j
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    /**
     * 创建索引
     *
     * @param index 索引名称
     */
    @Override
    public boolean createIndex(String index) throws IOException {
        // 判断索引是否存在
        if (this.existIndex(index)) {
            return true;
        }
        // 创建索引
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        return createIndexResponse.isAcknowledged();
    }

    /**
     * 判断索引是否存在
     *
     * @param index 索引名称
     */
    @Override
    public boolean existIndex(String index) throws IOException {
        // 判断索引是否存在
        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
        return restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
    }

    /**
     * 删除索引
     *
     * @param index 索引名称
     */
    @Override
    public boolean deleteIndex(String index) throws IOException {
        // 判断索引是否存在
        if (!this.existIndex(index)) {
            return true;
        }
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
        AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        return acknowledgedResponse.isAcknowledged();
    }

    /**
     * 新增文档
     *
     * @param index   索引名称
     * @param id      文档id
     * @param content 文档内容
     */
    @Override
    public boolean addDocument(String index, String id, String content) throws IOException {
        if (!this.createIndex(index)) {
            return false;
        }

        IndexRequest indexRequest = new IndexRequest(index);
        // 设置超时时间
        indexRequest.id(id);
        indexRequest.timeout(TimeValue.timeValueSeconds(1000000000));
        // 转换为json字符串
        indexRequest.source(content, XContentType.JSON);
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        return indexResponse.status().getStatus() == 200;
    }

    /**
     * 新增文档
     *
     * @param index   索引名称
     * @param jsonMap jsonMap
     */
    @Override
    public boolean addDocument(String index, String id, Map<String, Object> jsonMap) throws IOException {
        if (!this.createIndex(index)) {
            return false;
        }

        IndexRequest indexRequest = new IndexRequest(index).source(jsonMap);
        if (null != id) {
            indexRequest.id(id);
        }
        indexRequest.timeout(TimeValue.timeValueSeconds(1000000000));
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        log.info("indexResponse:{}", indexResponse);
        return indexResponse.status().getStatus() == 200;
    }

    /**
     * 判断是否存在文档
     *
     * @param index 索引名称
     * @param id    文档id
     */
    @Override
    public boolean isExistsDocument(String index, String id) throws IOException {
        // 判断是否存在文档
        GetRequest getRequest = new GetRequest(index, id);
        // 不获取返回的_source的上下文
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        return restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
    }

    /**
     * 获取文档
     *
     * @param index 索引名称
     * @param id    文档id
     */
    @Override
    public String getDocument(String index, String id) throws IOException {
        // 获取文档
        GetRequest getRequest = new GetRequest(index, id);
        GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        return getResponse.getSourceAsString();
    }

    /**
     * 更新文档
     *
     * @param index   索引名称
     * @param id      文档id
     * @param content 文档内容
     */
    @Override
    public boolean updateDocument(String index, String id, String content) throws IOException {
        // 更新文档
        UpdateRequest updateRequest = new UpdateRequest(index, id);
        updateRequest.timeout(TimeValue.timeValueSeconds(1));
        updateRequest.doc(content, XContentType.JSON);
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        return updateResponse.status().getStatus() == 200;
    }

    @Override
    public boolean updateDocument(String index, String id, Map<String, Object> jsonMap) throws IOException {
        // 更新文档
        UpdateRequest updateRequest = new UpdateRequest(index, id).doc(jsonMap);
        updateRequest.timeout(TimeValue.timeValueSeconds(1));
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        return updateResponse.status().getStatus() == 200;
    }

    /**
     * 删除文档
     *
     * @param index 索引名称
     * @param id    文档id
     */
    @Override
    public boolean deleteDocument(String index, String id) throws IOException {
        if (!this.isExistsDocument(index, id)) {
            return true;
        }

        // 删除文档
        DeleteRequest deleteRequest = new DeleteRequest(index, id);
        deleteRequest.timeout(TimeValue.timeValueSeconds(1));
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        return deleteResponse.status().getStatus() == 200;
    }

    /**
     * 批量插入
     *
     * @param index    索引名称
     * @param contents 文档内容
     */
    @Override
    public <T> boolean bulkRequest(String index, List<T> contents) throws IOException {
        // 批量插入
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout(TimeValue.timeValueSeconds(1));
        contents.forEach(x -> bulkRequest.add(
                new IndexRequest(index)
                        .source(JsonUtils.objectToJson(x), XContentType.JSON)));
        BulkResponse bulkItemResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        log.info("bulkItemResponse:{}", bulkItemResponse.getItems());
        return !bulkItemResponse.hasFailures();
    }

    /**
     * 搜索请求
     *
     * @param index   索引名称
     * @param keyword 关键字
     */
    @Override
    public List<Map<String, Object>> searchRequest(String index, String keyword) throws IOException {
        // 搜索请求
        SearchRequest searchRequest;
        if (StringUtils.isEmpty(index)) {
            searchRequest = new SearchRequest();
        } else {
            searchRequest = new SearchRequest(index);
        }
        // 条件构造
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 第几页
        searchSourceBuilder.from(0);
        // 每页多少条数据
        searchSourceBuilder.size(1000);
        // 配置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("deptName").field("location");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);
        // 精确查询
//        QueryBuilders.termQuery();
        // 匹配所有
//        QueryBuilders.matchAllQuery();
        // 最细粒度划分：ik_max_word，最粗粒度划分：ik_smart
        //multiMatchQuery多字段匹配查询
        searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keyword, "deptNo", "deptName", "location", "id"));
        // matchQuery 单字段匹配一个值
//        searchSourceBuilder.query(QueryBuilders.matchQuery("content", keyWord));
        searchSourceBuilder.timeout(TimeValue.timeValueSeconds(10));

        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<Map<String, Object>> results = new ArrayList<>();
        for (SearchHit searchHit : searchResponse.getHits().getHits()) {
            Map<String, HighlightField> highlightFieldMap = searchHit.getHighlightFields();
            HighlightField deptName = highlightFieldMap.get("deptName");
            HighlightField location = highlightFieldMap.get("location");
            // 原来的结果
            Map<String, Object> sourceMap = searchHit.getSourceAsMap();
            // 解析高亮字段，替换掉原来的字段
            if (deptName != null) {
                Text[] fragments = deptName.getFragments();
                StringBuilder nTitle = new StringBuilder();
                for (Text text : fragments) {
                    nTitle.append(text);
                }
                sourceMap.put("deptName", nTitle.toString());
            }
            if (location != null) {
                Text[] fragments = location.getFragments();
                StringBuilder nDescription = new StringBuilder();
                for (Text text : fragments) {
                    nDescription.append(text);
                }
                sourceMap.put("location", nDescription.toString());
            }
            results.add(sourceMap);
        }
        return results;
    }

    /**
     * 搜索所有id
     *
     * @param index 索引名称
     */
    @Override
    public List<String> searchAllRequest(String index) throws IOException {
        // 搜索请求
        SearchRequest searchRequest;
        if (StringUtils.isEmpty(index)) {
            searchRequest = new SearchRequest();
        } else {
            searchRequest = new SearchRequest(index);
        }
        // 条件构造
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 第几页
        searchSourceBuilder.from(0);
        // 每页多少条数据
        searchSourceBuilder.size(1000);
        // 匹配所有
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchSourceBuilder.timeout(TimeValue.timeValueSeconds(10));

        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<String> results = new ArrayList<>();
        for (SearchHit searchHit : searchResponse.getHits().getHits()) {
            results.add(searchHit.getId());
        }
        return results;
    }
}

